#include "Basic/UnitTest.h"
#include "Collections/List.h"
#include "Collections/Dictionary.h"
#include "Collections/Linq.h"

#include <algorithm>
using namespace zl;
using namespace zl::collections;


struct person
{
	std::string		name;
};

struct pet
{
	std::string		name;
	person		    owner;
};

template<typename TList>
void  TestReadOnlyList(const TList& list,zint* item,zint count)
{
	assert(list.size()==count);
	zint* _item = item;
	zint  index = 0;
	for (typename TList::iterator _it = list.begin(); _it != list.end(); _it++,_item++,index++)
	{
		assert(list.contains(*_item));
		assert(*_it = *_item);
		assert(list.firstindexof(*_item)==index);
		assert(list.lastindexof(*_item) == index);
		assert(list[index] == *_item);
		assert(list.get(index) == *_item);
	}
}

TEST_CASE(TestArray)
{
	{
		Array<zint> arr = {1,2,3,4,5};
		zint _arr[] = {1,2,3,4,5};
		zint __arr[] = { 1, 2, 3, 4, 5, 6 };
		zint ___arr[] = { 1, 2, 3, 4, 5, 6, 7 };
		TestReadOnlyList(arr,_arr,5);
		Array<zint> arr_(arr);
		TestReadOnlyList(arr_, _arr, 5);
		Array<zint> arr__(arr.begin(), arr.end());
		TestReadOnlyList(arr__, _arr, 5);
		arr_ = {1,2,3,4,5,6};
		TestReadOnlyList(arr_,__arr,6);
		arr_.resize(7);
		arr_[6] = 7;
		TestReadOnlyList(arr_,___arr,7);
	}
}

TEST_CASE(TestList)
{
	{
		List<zint>  list = { 1, 2, 3, 4, 5, 6 };
		zint arr[] = { 1, 2, 3, 4, 5, 6 };
		TestReadOnlyList(list, arr, 6);

		List<zint>  _list(list);
		TestReadOnlyList(_list, arr, 6);

		List<zint>  __list(list.begin(), list.begin() + 5);
		zint _arr[] = { 1, 2, 3, 4, 5 };
		TestReadOnlyList(__list, _arr, 5);

		list = __list;
		TestReadOnlyList(list, _arr, 5);
		list = {1,2,3,4,5};
		TestReadOnlyList(list, _arr, 5);

		List<zint> list_ = _list;
		TestReadOnlyList(list_, arr, 6);
	}
	
	{
	     List<zint>  list;
		 list.add(1);
		 list.add(2);
		 list.add(3);
		 list.add(4);
		 list.add(5);
		 zint arr_[] = {1,2,3,4,5};
		 TestReadOnlyList(list, arr_, 5);
		 list[1] = 7;
		 zint arr__[] = { 1, 7, 3, 4 ,5};
		 TestReadOnlyList(list, arr__, 5);
		 list.set(1, 6);
		 zint arr___[] = {1,6,3,4,5};
		 TestReadOnlyList(list,arr___,5);
		 list.remove(4);
		 zint arr____[] = { 1, 6, 3 ,5};
		 TestReadOnlyList(list, arr____, 4);
		 list.removeat(0);
		 zint _arr[] = { 6, 3, 5 };
		 TestReadOnlyList(list, _arr, 3);
		 list.removerange(1,2);
		 zint __arr[] = {6};
		 TestReadOnlyList(list,__arr,1);
		 list.clear();
		 TestReadOnlyList(list,(zint*)0,0);
		 assert(list.capacity() == 0);
		 list.add(3);
		 list.add(5);
		 list.add(3);
		 list.remove(3);
		 zint ___arr[] = {5};
		 TestReadOnlyList(list,___arr,1);
    }
}

TEST_CASE(TestSortedList)
{
	{
		SortedList<zint>  list;
		list = { 7,6,3,5,10,22 };
		zint arr[] = { 3,5,6,7,10,22 };
		TestReadOnlyList(list, arr, 6);
		std::sort(list.begin(), list.end());

		SortedList<zint>  _list(list);
		TestReadOnlyList(_list, arr, 6);

		SortedList<zint>  __list(list.begin(), list.begin() + 5);
		zint _arr[] = { 3, 5, 6, 7, 10 };
		TestReadOnlyList(__list, _arr, 5);

		list = __list;
		TestReadOnlyList(list, _arr, 5);
		list = { 10, 5, 7, 6, 3 };
		TestReadOnlyList(list, _arr, 5);

		SortedList<zint> list_ = _list;
		TestReadOnlyList(list_, arr, 6);
	}

	{
	    SortedList<zint>  list;
		list.add(5);
		list.add(3);
	    list.add(1);
	    list.add(2);
	    list.add(4);
	    zint arr_[] = { 1, 2, 3, 4, 5 };
	    TestReadOnlyList(list, arr_, 5);
	    list.remove(4);
	    zint arr____[] = { 1,2,3,5 };
	    TestReadOnlyList(list, arr____, 4);
	    list.removeat(0);
	    zint _arr[] = { 2,3,5 };
	    TestReadOnlyList(list, _arr, 3);
	    list.removerange(1, 2);
	    zint __arr[] = { 2 };
	    TestReadOnlyList(list, __arr, 1);
	    list.clear();
	    TestReadOnlyList(list, (zint*)0, 0);
	    assert(list.capacity() == 0);
	    list.add(3);
	    list.add(5);
	    list.add(3);
	    list.remove(3);
	    zint ___arr[] = { 5 };
	    TestReadOnlyList(list, ___arr, 1);
    }
}

template<typename K,typename V>
void  TestReadOnlyDic(const Dictionary<K,V>& dic,K* keys,zint keyslen,V* values,zint valueslen)
{
	assert(dic.size() == keyslen);
	assert(dic.keys().size() == keyslen);
	assert(dic.values().size() == valueslen);
	for (typename Dictionary<K, V>::iterator _it = dic.begin();
		_it != dic.end(); _it++,keys++,values++)
	{
		assert((*_it).key==*keys);
		assert((*_it).value == *values);
		assert(dic[*keys]==*values);
		assert(dic.get(*keys) == *values);
	}
}

template<typename K, typename V>
void  TestReadOnlyGroup(const Group<K, V>& dic, K* keys, zint keyslen, List<V> values)
{
	assert(dic.size() == keyslen);
	assert(dic.keys().size() == keyslen);
	zint index = 0;

	for (typename Group<K, V>::iterator _it = dic.begin();
		_it != dic.end(); _it++, keys++,index++)
	{
		assert(dic.contains(*keys));
		assert((*_it).key == *keys);
		assert((*_it).value == values[index]);

		typename List<V>::iterator _vit = values.begin();
		for (typename List<V>::iterator _kit = dic[*keys].begin();
			_kit != dic[*keys].end(); _kit++,_vit++)
		{
			assert(*_kit==*_vit);
			assert(dic.contains(*keys,*_vit));
		}

		typename List<V>::iterator __vit = values.begin();
		for (typename List<V>::iterator __kit = dic.get(*keys).begin();
			__kit != dic.get(*keys).end();__kit++,__vit++)
		{
			assert(*__kit == *__vit);
			assert(dic.contains(*keys, *__vit));
		}

		typename List<V>::iterator ___vit = values.begin();
		for (typename List<V>::iterator ___kit = dic.getbyindex(index).begin();
			___kit != dic.getbyindex(index).end(); ___kit++, ___vit++)
		{
			assert(*___kit == *___vit);
			assert(dic.contains(*keys, *___vit));
		}
	}
}

TEST_CASE(TestDictionary)
{
	{
		Dictionary<zint, std::string> dic;
		dic.add(1, "123");
		dic.add(2, "456");
		zint arr[] = {1,2};
		std::string str[] = {"123","456"};
		TestReadOnlyDic(dic,arr,2,str,2);

		Dictionary<zint, std::string> _dic(dic.begin(), dic.end());
		TestReadOnlyDic(_dic, arr, 2, str, 2);
		Dictionary<zint, std::string>  __dic=dic;
		TestReadOnlyDic(_dic, arr, 2, str, 2);

		assert(dic.set(3,"789"));
		dic[2] = "0123";
		zint arr_[] = { 1, 2, 3 };
		std::string str_[] = { "123", "0123" ,"789"};
		TestReadOnlyDic(dic,arr_,3,str_,3);

		assert(dic.set(3, "7890"));
		std::string str__[] = { "123", "0123", "7890" };
		TestReadOnlyDic(dic, arr_, 3, str__, 3);

		assert(!dic.remove(4));
		assert(dic.remove(2));
		zint arr__[] = {1,3};
		std::string str___[] = {"123","7890"};
		TestReadOnlyDic(dic,arr__,2,str___,2);

		dic.clear();
		TestReadOnlyDic(dic, (zint*)0, 0, (std::string*)0, 0);
	}
}

TEST_CASE(TestGroup)
{
	Group<zint, std::string > group;
	assert(group.add(1, "123"));
	assert(group.add(2, "234"));
	assert(group.add(3, "345"));
	zint arr[] = {1,2,3};
	List<std::string>  str = { "123", "234", "345" };
	TestReadOnlyGroup(group,arr,3,str);

}

TEST_CASE(TestLinqWithZL)
{
	List<zint> v = { 1, 3, 2, 2 };
	{
		auto res = from(v).where([&](zint i){return i > 1; }).distinct().to_zl_list();
		zint arr[] = { 3, 2 };
		TestReadOnlyList(res,arr,2);
	}
}


TEST_CASE(TestLinqWithSTL)
{
	std::vector<zint> v;
	v.push_back(1);
	v.push_back(3);
	v.push_back(2);
	v.push_back(2);
	{
		auto res = from(v).where([&](zint i){return i > 1; });
		zint index = 0;
		zint arr[] = {3,2,2};
		for (auto i : res)
		{
			assert(i==arr[index]);
			index++;
		}
	}
	{
		auto res = from(v).select([&](zint i){return i * 2; });
		zint arr[] = { 2, 6, 4, 4 };
		zint index = 0;
		for (auto i : res)
		{
			assert(i == arr[index]);
			index++;
		}
	}
	{
		std::vector<zint> v2;
		v2.push_back(1);
		v2.push_back(3);
		v2.push_back(2);
		v2.push_back(2);
		{
			auto res = from(v).concat(from(v2));
			zint arr[] = { 1, 3, 2, 2, 1, 3, 2, 2 };
			zint index = 0;
			for (auto i : res)
			{
				assert(i==arr[index]);
				index++;
			}
		}
		{
			auto res = from(v).concat(v2);
			zint arr[] = { 1, 3, 2, 2, 1, 3, 2, 2 };
			zint index = 0;
			for (auto i : res)
			{
				assert(i == arr[index]);
				index++;
			}
		}
		{
			auto res = from(v).concat({ 1, 2, 3 });
			zint arr[] = { 1, 3, 2, 2, 1, 2, 3 };
			zint index = 0;
			for (auto i : res)
			{
				assert(i == arr[index]);
				index++;
			}
		}
	}
	{
		auto res = from(v).skip(2);
		zint arr[] = { 2, 2 };
		zint index = 0;
		for (auto i : res)
		{
			assert(i == arr[index]);
			index++;
		}
	}
	{
		auto res = from(v).skip_while([](int i){return i % 2 == 1; });
		zint arr[] = { 2, 2 };
		zint index = 0;
		for (auto i : res)
		{
			assert(i == arr[index]);
			index++;
		}
	}
	{
		auto res = from(v).take(2);
		zint arr[] = { 1, 3 };
		zint index = 0;
		for (auto i : res)
		{
			assert(i == arr[index]);
			index++;
		}
	}
	{
		auto res = from(v).take_while([](int i){return i % 2 == 1; });
		zint arr[] = { 1, 3 };
		zint index = 0;
		for (auto i : res)
		{
			assert(i == arr[index]);
			index++;
		}
	}
	{
		{
			auto res = from(v).aggregate([](int a, int b){return a + b; });
			assert(res==8);
		}
		{
		    auto res = from(v).aggregate(5, [](int a, int b){return a + b; });
		    assert(res==13);
	    }
		{
			auto res = from(v).all([](zint a){return a % 2 == 1; });
			assert(!res);
		}
		{
			auto res = from(v).any([](zint a){return a % 2 == 1; });
			assert(res==true);
		}
	}
	{
		assert(from(v).max() == 3);
		assert(from(v).min() == 1);
		assert(from(v).count() == 4);
		assert(from(v).average<double>() == 2.0);
		assert(from(v).contains(2));
		assert(from(v).first() == 1);
		assert(from(v).last() == 2);
		assert(from(v).sum() == 8);
		assert(from(v).product() == 12);
		auto res = from(v).distinct();
		zint arr[] = {1,3,2};
		zint index = 0;
		for (auto i : res)
		{
			assert(i==arr[index]);
			index++;
		}
	}
	{
		auto res = from(v).order_by([](zint x){return x; });
		zint arr[] = {1,2,2,3};
		zint index = 0;
		for (auto i : res)
		{
			assert(i==arr[index]);
			index++;
		}
	}
	{
		auto res = from(v).group_by([](int x){return x % 2; });
		zint key[] = {0,1};
		zint value[] = { 2, 2,1,3};
		zint ki = 0;
		zint vi = 0;
		for (auto i : res)
		{
			assert(i.first==key[ki]);
			ki++;
			for (auto j : i.second)
			{
				assert(j==value[vi]);
				vi++;
			}
		}
	}
	{
		int yz[] = { 3, 5, 6 };
		auto res = from(v).except_with(yz);
		zint arr[] = { 1, 2, 2 };
		zint index = 0;
		for (auto i : res)
		{
			assert(i==arr[index]);
			index++;
		}
	}
	{
		int yz[] = { 3, 5, 6 };
		auto res = from(v).intersect_with(yz);
		zint arr[] = {3};
		zint index = 0;
		for (auto i : res)
		{
			assert(i == arr[index]);
			index++;
		}
	}
	{
		int yz[] = { 3, 5, 6 };
		auto res = from(v).union_with(yz);
		zint arr[] = { 1,3,2,5,6 };
		zint index = 0;
		for (auto i : res)
		{
			assert(i == arr[index]);
			index++;
		}
	}
	{
		auto res = from(v).select_many([](int x){return from_values({ x, x*x, x*x*x }); });
		zint arr[] = { 1, 1, 1, 3, 9, 27, 2, 4, 8, 2, 4, 8 };
		zint index = 0;
		for (auto i : res)
		{
			assert(i==arr[index]);
			index++;
		}
	}
	{
		auto res = from(v).sequence_equal({ 1, 3, 2, 2 });
		assert(res);
	}

	{
		person magnus = { "Hedlund, Magnus" };
		person terry = { "Adams, Terry" };
		person charlotte = { "Weiss, Charlotte" };
		person persons[] = { magnus, terry, charlotte };

		pet barley = { "Barley", terry };
		pet boots = { "Boots", terry };
		pet whiskers = { "Whiskers", charlotte };
		pet daisy = { "Daisy", magnus };
		pet pets[] = { barley, boots, whiskers, daisy };

		auto person_name = [](const person& p){return p.name; };
		auto pet_name = [](const pet& p){return p.name; };
		auto pet_owner_name = [](const pet& p){return p.owner.name; };

		auto f = from(persons).full_join(from(pets), person_name, pet_owner_name);
		{
			typedef join_pair<std::string, linq<person>, linq<pet>> TItem;
			auto xs = f.to_std_vector();
			assert(from(xs).select([](const TItem& item){return item.first; }).sequence_equal({ terry.name, magnus.name, charlotte.name }));
			assert(xs[0].second.first.select(person_name).sequence_equal({ terry.name }));
			assert(xs[1].second.first.select(person_name).sequence_equal({ magnus.name }));
			assert(xs[2].second.first.select(person_name).sequence_equal({ charlotte.name }));
			assert(xs[0].second.second.select(pet_name).sequence_equal({ barley.name, boots.name }));
			assert(xs[1].second.second.select(pet_name).sequence_equal({ daisy.name }));
			assert(xs[2].second.second.select(pet_name).sequence_equal({ whiskers.name }));
		}
		auto g = from(persons).group_join(from(pets), person_name, pet_owner_name);
		{
			typedef join_pair<std::string, person, linq<pet>> TItem;
			auto xs = g.to_std_vector();
			assert(from(xs).select([](const TItem& item){return item.first; }).sequence_equal({ terry.name, magnus.name, charlotte.name }));
			assert(xs[0].second.first.name == terry.name);
			assert(xs[1].second.first.name == magnus.name);
			assert(xs[2].second.first.name == charlotte.name);
			assert(xs[0].second.second.select(pet_name).sequence_equal({ barley.name, boots.name }));
			assert(xs[1].second.second.select(pet_name).sequence_equal({ daisy.name }));
			assert(xs[2].second.second.select(pet_name).sequence_equal({ whiskers.name }));
		}
		auto j = from(persons).join(from(pets), person_name, pet_owner_name);
		{
			typedef join_pair<std::string, person, pet> TItem;
			auto xs = j.to_std_vector();
			assert(from(xs).select([](const TItem& item){return item.first; }).sequence_equal({ terry.name, terry.name, magnus.name, charlotte.name }));
			assert(xs[0].second.first.name == terry.name);
			assert(xs[1].second.first.name == terry.name);
			assert(xs[2].second.first.name == magnus.name);
			assert(xs[3].second.first.name == charlotte.name);
			assert(xs[0].second.second.name == barley.name);
			assert(xs[1].second.second.name == boots.name);
			assert(xs[2].second.second.name == daisy.name);
			assert(xs[3].second.second.name == whiskers.name);
		}
	}
}